Skip to content

Myanglog

데이터 중심 애플리케이션 설계 1장 정리

4 min read

2021년 11월부터 마틴 클레프만의 '데이터 중심 어플리케이션 설계' 라는 책을 스터디하고 있다. 굉장히 컴팩트하고 심도있게 다루는 책이라 소화가 덜된 부분이 많다. 스터디는 끝나가지만 복습 차원에서 특히 중요했던 장들을 다시 정리해보려고 한다.

Preface

데이터베이스, 분산 시스템 분야 발전의 원동력들

  • 엄청난 양의 데이터와 트래픽을 다루게 됨 → 새로운 도구를 만들어야 했음
  • 기업은 애자일하고, 빠르게 시장을 간파해 대처해야함
  • 자유 오픈소스 소프트웨어
  • CPU 클록 속도는 거의 증가 x, 멀티 코어 프로세서가 표준이며 네트워크는 빨라지고 있음 → 병렬 처리는 계속 늘고있다
  • AWS같은 IaaS 덕분에 소규모 팀에서도 여러 장비에 분산 시스템을 개발할 수 있음
  • 서비스에 고가용성을 요구하게 됨

데이터중심 애플리케이션(data-intensive application)

데이터 양, 복잡성, 데이터가 변하는 속도 등 데이터가 주요 도전 과제인 어플리케이션.

↔ 계산중심적(compute-intensive) : CPU 사이클이 병목

책에서 학습할 내용

  • 급격한 기술변화, 유행과 상관없이 변하지 않는 원리에 대해
  • 데이터 시스템의 핵심 알고리즘과 원리, 기술들의 트레이드오프
    • 데이터 시스템의 동작 방식 + 왜 그렇게 동작하는지
  • 특정 목적에 어떤 기술이 적합한지 결정하는 능력 + 좋은 애플리케이션 아키텍처의 기반을 위해 도구를 조합하는 방법

참고자료 모음

https://github.com/ept/ddia-references

1장. 신뢰할 수 있고 확장 가능하며 유지보수하기 쉬운 애플리케이션 (reliable, scalable, maintainable systems)

책 전반에 걸쳐 사용하는 용어 및 접근 방식에 대해 다룬다.

데이터 시스템에 대한 생각

  • DB, 큐, 캐시 등 모든것을 데이터 시스템 이라는 포괄적 용어로 묶은 이유

    • 새로운 도구들은 다양한 usecase에 최적화되었고, 더이상 전통적 분류에 딱 들어맞지 않음
      • redis는 메시지큐를 사용하는 datastore, Apache Kafka는 지속성을 보장하는 메시지 큐 etc.
    • 단일 도구로는 데이터 처리와 저장 모두를 만족시킬 수 없는 광법위한 요구사항들이 생겨남
  • 좋은 데이터 시스템 설계를 위해 고려할 세가지

    • 신뢰성(reliability)
    • 확장성(scalability)
    • 유지보수성(maintainability)

신뢰성

  • "무언가 잘못되더라도 지속적으로 올바르게 동작함"

    • 애플리케이션은 기대한 기능을 수행한다.
    • 시스템은 예상치 못한 사용에도 대처할 수 있다.
    • 성능은 예상된 부하와 데이터 양에서 충분한 사용례를 만족한다.
    • 시스템은 허가되지 않은 접근과 오남용을 방지한다.
  • 결함(fault) : 잘못될 수 있는 일

    • 사양에서 벗어난 시스템의 한 구성 요소
    • ≠ 장애 : 사용자에게 서비스를 제공하지 못하고 시스템 전체가 멈춘 경우.
  • 내결함성(fault-tolerant) or 탄력성(resilient) : 결함을 예측하고 대처할 수 있는

  • 결함확률이 0인건 불가능하지만, 결함으로 인해 장애가 발생하지 않게끔 설계하는게 중요.

각 결함 유형과 해결책들

  • 하드웨어 결함
    • 고장
    • 해결책
      • → 각 하드웨어 구성요소에 중복을 추가하기
        • 디스크 RAID 구성
        • 서버 이중전원 디바이스, hot-swap가능한 CPU
        • 하나가 죽으면 교체되는동안 중복된 구성요소를 대신 사용
    • 최근에는 데이터 양, 계산량 증가 → 더 많은 수의 장비 → 하드웨어 결함율 증가
      • AWS 같은 클라우드 플랫폼은 단일 장비 신뢰성보다 유연성, 탄력성을 우선 처리.
      • → 소프트웨어 내결함성 기술 사용하거나, 하드웨어 중복성을 추가해 전체 장비의 손실을 견딜 수 있는 시스템으로 옮겨가는 추세.
  • 소프트웨어 결함
    • system 내 체계적 오류: 무작위적이고 서로 독립적인 하드웨어 결함과 달리, 노드간 상관관계 때문에 시스템 오류를 더욱 많이 유발함.
      • 리눅스 커널 버그 - 2012년 6월 30일 윤초
      • CPU 시간 / 메모리 / 디스크 등 공유자원을 과도하게 사용하는 일부 프로세스
      • 시스템의 속도가 느려져 반응이 없거나 잘못된 응답을 반환하는 서비스
      • 하나 요소의 결함이 다른 구성 요소의 결함을 야기하고 ... → 연쇄 장애
    • 특정 상황에 의해 발생하기 전까지 오랫동안 나타나지 않음
    • 해결책
      • 신속한 해결책은 없다 🥲
      • 시스템의 가정과 상호작용에 대해서 주의깊게 생각하기
      • 빈틈없는 테스트
      • 프로세스 격리
      • 모니터링
  • 인적 오류
    • 인적 오류를 최소화할 수 있는 설계
      • 오류의 가능성을 최소화하는 방향으로 시스템을 설계하라. 잘 설계된 추상화, API, 관리 인터페이스를 사용하자.
      • 실제 데이터를 볼 수 있지만 사용자에게는 영향이 없는 비-production 환경의 샌드박스를 제공하라.
      • 단위 테스트, 전체 시스템 통합 테스트, 수동 테스트까지 모든 수준에서 철저히 테스트하라.
      • 설정 변경은 빠르게 롤백 가능하게, 새로운 코드는 서서히 롤 아웃
      • 성능 지표와 오류 rate 같은 모니터링
      • 조작 교육과 실습

확장성

부하 증가에도 안정적으로 동작할 수 있는가

확장성은 있다/없다 가 아니라, '특정 방식으로 시스템이 커지면 어떻게 대처할까', '추가 부하를 다루기 위해 자원을 어떻게 투입할까'를 고민하는 문제.

부하 기술하기

  • 부하 매개변수(load parameter) 로 나타낼 수 있다
    • 무엇이 부하 매개 변수가 되는가
      • 웹 서버의 초당 요청 수
      • DB의 읽기/쓰기 비율
      • 대화방의 동시 활성 사용자
      • 캐시 적중률
    • 부하 매개변수의 평균이 중요할 수도, 소수의 극단적 케이스가 중요할 수도 있다.
      • ex. 2012년의 트위터
        • 주요 두가지 동작
          • 트윗 작성
            • 팔로워에게 새로운 메시지를 게시. (평균 초당 4.6k 요청. 피크일때 초당 12k 이상)
          • 홈 타임라인 (read)
            • 팔로우한 사람이 작성한 트윗을 볼 수 있다.(평균 초당 300k요청)
        • 개별 사용자가 트윗 작성을 하면 많은 팔로워들의 타임라인에 함께 들어가야 함. - fan-out (하나의 수신 요청을 처리하는 데 필요한 다른 서비스의 요청 수)
        • 어떻게 해결? (1→ 2→ 3으로 발전함)
          1. 트윗 작성은 새로운 트윗을 전역 컬렉션에 삽입한다. 홈 타임라인 read를 요청하면 팔로우하는 모든 사람을 찾고, 그 사람들의 모든 트윗을 찾아 시간순 정렬해서 merge. → 읽기 시점에 비용이 큼.
          2. 각 사용자용 홈 타임라인 캐시를 유지한다. 사용자가 트윗을 작성하면 해당 사용자의 팔로워를 찾아서 캐시에 새로운 트윗을 삽입한다. → 쓰기시점에 미리 계산하여 읽기 시점의 계산량을 줄임 (읽기 요청량에 비해 쓰기 요청량이 적으므로 합리적)
          3. hybrid: 대부분 2번처럼 홈 타임라인에 넣어주지만, 팔로워 수가 매우 많은 사용자는 팬 아웃에서 제외하고 별도로 가져와 읽는 시점에 사용자의 홈 타임라인에 합치기 → 팔로워 수가 많으면 단일 쓰기가 과한 작업이 되어서 선택

성능 기술하기

  • output에 대해 어떻게 측정할 것인가

    • 부하 매개변수를 증가시켰을 때 성능은 얼마나 영향을 받을까?
    • 성능이 유지되길 원하면 자원을 얼마나 많이 늘려야할까?
  • 중요한 성능 수치

    • 일괄 처리 시스템 - 처리량(throughput) : 초당 처리할 수 있는 레코드 수/데이터집합. 작업 수행에 걸리는 전체 시간
    • 온라인 시스템 - response time : 클라이언트가 요청을 보내고 응답을 받는 사이의 시간.
  • 응답 시간은 동일한 요청에도 매번 다르므로, 단일 숫자가 아닌 측정 가능한 값의 분포 로 생각해야함
  • 산술 평균은 좋은 지표가 아니다. 얼마나 많은 사용자가 실제로 지연을 경험했는지가 중요.
    • → 백분위와 중앙값(median) 사용이 적절함
      • 가장 빠른 시간부터 가장 느린 시간까지 정렬.
      • 사용자 요청의 절반은 중앙값보다 오래 걸린다.
      • 상위 백분위 응답시간(tail latency) 으로 특이 값(outlier)이 얼마나 안좋은지 판단: 95분위, 99분위, 99.9분위...
        • Amazon: 99.9분위를 쓴다. 1000개 중 1개 영향이지만, 그 1명의 고객이 계정에 가장 많은 데이터를 갖고있는 VIP들이므로 만족도 지표에 영향
      • 상위 백분위에서 소수의 느린 요청 처리로 후속 처리가 지체 : 선두 차단(head-of-line blocking) 문제
        • → 클라이언트 쪽 응답시간 측정이 중요하다.
        • 부하측정 테스트 시 응답 시간과 독립적으로 요청을 계속 보내야 함. 이전 요청 완료까지 기다렸다가 다음요청을 보내면 평가를 왜곡함

부하 대응 접근방식

  • Scale up (용량 확장, 수직 확장)
  • Scale out (규모 확장, 수평 확장)

실용적인 접근방식의 조합이 필요하다.

  • 단일 노드 상태유지(stateful) 데이터 시스템은 분산 설치가 복잡도가 큼 → 고가용성 요구가 있을 때까지 단일 노드에 DB를 유지하여 scale up 하는게 통념.

    • 최근에 분산 시스템을 위한 도구, 추상화가 좋아져서 통념이 바뀌고 있음
  • 부하 증가를 감지하면, 컴퓨팅 자원을 자동으로 추가해주는 탄력적 시스템은 부하 예측이 어려울 경우 유용, but 수동으로 확장하는 시스템이 더 간단하고 운영상 예상치 못한 일이 더 적음.

  • 범용적이고 모든 상황에 맞는 만병통치약 같은 확장 아키텍처는 없다.

    • 아키텍처를 결정하는 요소들
      • 읽기, 쓰기 양
      • 저장할 데이터 양
      • 데이터 복잡도
      • 응답시간 요구사항
      • ...
    • 주요 동작이 무엇이고, 잘 하지 않는 동작이 무엇인지에 대한 가정이 어떤 아키텍처를 갖출 것이냐에 매우 중요

유지보수성

유지보수의 고통을 줄이고 레거시를 만들지 않게 설계하자

운용성: 운영의 편리함 만들기

  • 동일하게 반복되는 태스크를 쉽게 수행하도록 해서, 운영팀이 더 부가가치 높은 일에 노력을 집중할 수 있게 하자.
    • 모니터링
    • 자동화와 통합을 위한 도구
    • 개별장비 의존성x. 유지보수를 위해 장비를 내리더라도 시스템에 영향 없게
    • 문서, 이해가 쉬운 운영모델
    • ...

단순성: 복잡도 관리

  • 우발적인 복잡도 제거를 위해 좋은 추상화를.
  • ex. 고수준 프로그래밍언어는 기계어, CPU레지스터, 시스템 호출을 숨김

발전성: 변화를 쉽게 만들기

  • 요구사항은 끊임없이 발전한다.
  • 애자일
    • TDD
    • refactoring
  • 간단하고 이해하기 쉬운 시스템을 만들기